package com.swak.reactivex.threads;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.swak.reactivex.future.CompletableFutureTask;

public abstract class AbstractEventLoop implements EventLoop {

	protected static Logger logger = LoggerFactory.getLogger(AbstractEventLoop.class);
	static final long DEFAULT_SHUTDOWN_QUIET_PERIOD = 2;
	static final long DEFAULT_SHUTDOWN_TIMEOUT = 15;

	@Override
	public CompletableFuture<Void> shutdownGracefully() {
		return shutdownGracefully(DEFAULT_SHUTDOWN_QUIET_PERIOD, DEFAULT_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS);
	}

	@Override
	public boolean inEventLoop() {
		return inEventLoop(Thread.currentThread());
	}

	@Override
	public CompletableFuture<Void> submit(Runnable task) {
		CompletableFutureTask<Void> futureTask = new CompletableFutureTask<>(task, null);
		this.execute(futureTask);
		return futureTask;
	}

	@Override
	public <T> CompletableFuture<T> submit(Runnable task, T result) {
		CompletableFutureTask<T> futureTask = new CompletableFutureTask<>(task, result);
		this.execute(futureTask);
		return futureTask;
	}

	public abstract void lazyExecute(Runnable task);

	protected abstract void run();

	/**
	 * Marker interface for {@link Runnable} to indicate that it should be queued for execution
	 * but does not need to run immediately.
	 */
	public interface LazyRunnable extends Runnable {
	}

	/**
	 * Try to execute the given {@link Runnable} and just log if it throws a {@link Throwable}.
	 */
	protected static void safeExecute(Runnable task) {
		try {
			task.run();
		} catch (Throwable t) {
			logger.warn("A task raised an exception. Task: {}", task, t);
		}
	}

	protected static void handleLoopException(Throwable t) {
		logger.warn("Unexpected exception in the selector loop.", t);

		// Prevent possible consecutive immediate failures that lead to
		// excessive CPU consumption.
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// Ignore.
		}
	}

	protected static void reject() {
		throw new RejectedExecutionException("event executor terminated");
	}

	/**
	 * Offers the task to the associated {@link RejectedExecutionHandler}.
	 *
	 * @param task to reject.
	 */
	protected final void reject(Runnable task) {
		throw new RejectedExecutionException("event executor terminated");
	}

}
